package bsh.meta;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.objectweb.asm.Type;

/**
 * @author takeshi
 */
public class MetaTypeWrapper extends AbstractTypeWrapper implements TypeWrapper {

    private final TypeWrapper classWrapper = JavaClassWrapper.wrapperFor(Class.class);

    private final TypeWrapper literalType;

    private final Set<MethodDescriptor> methods = new HashSet<MethodDescriptor>();

    private final Set<FieldDescriptor> fields = new HashSet<FieldDescriptor>();

    /**
     * @param literalType {@link TypeWrapper}
     */
    public MetaTypeWrapper(TypeWrapper literalType) {
        super();
        this.literalType = literalType;
        this.findStatics();
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.meta.AbstractTypeWrapper#toASMType()
     */
    @Override
    public Type toASMType() {
        return literalType.toASMType();
    }

    /**
     * @param javaType Class
     */
    public MetaTypeWrapper(Class<?> javaType) {
        this(JavaClassWrapper.wrapperFor(javaType));
    }

    /**
     * 
     */
    private void findStatics() {
        for (MethodDescriptor m : this.literalType.visibleMethods()) {
            if (Modifier.isStatic(m.modifiers())) {
                this.methods.add(m);
            }
        }
        for (FieldDescriptor fd : this.literalType.visibleFields()) {
            if (Modifier.isStatic(fd.asmModifiers())) {
                this.fields.add(fd);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public String className() {
        return this.classWrapper.className();
    }

    /**
     * {@inheritDoc}
     */
    public List<? extends TypeWrapper> interfaces() {
        return this.classWrapper.interfaces();
    }

    /**
     * {@inheritDoc}
     */
    public String internalName() {
        return this.classWrapper.internalName();
    }

    /**
     * {@inheritDoc}
     */
    public boolean isInterface() {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    public TypeWrapper superType() {
        return JavaClassWrapper.OBJECT_WRAPPER;
    }

    /**
     * {@inheritDoc}
     */
    public String typeDescription() {
        return this.classWrapper.typeDescription();
    }

    /**
     * {@inheritDoc}
     */
    public Collection<? extends FieldDescriptor> visibleFields() {
        return new ArrayList<FieldDescriptor>(this.fields);
    }

    /**
     * {@inheritDoc}
     */
    public Collection<? extends MethodDescriptor> visibleMethods() {
        return new ArrayList<MethodDescriptor>(this.methods);
    }

    /**
     * {@inheritDoc}
     */
    public Collection<? extends List<TypeWrapper>> visibleConstructors() {
        return Collections.emptyList();
    }

    /**
     * {@inheritDoc}
     */

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.literalType == null) ? 0 : this.literalType.hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     */

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        MetaTypeWrapper other = (MetaTypeWrapper) obj;
        if (this.literalType == null) {
            if (other.literalType != null) {
                return false;
            }
        } else if (!this.literalType.equals(other.literalType)) {
            return false;
        }
        return true;
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.meta.TypeWrapper#protectedFields()
     */
    public Collection<? extends FieldDescriptor> protectedFields() {
        return Collections.emptyList();
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.meta.TypeWrapper#protectedMethods()
     */
    public Collection<? extends MethodDescriptor> protectedMethods() {
        return Collections.emptyList();
    }

    /**
     * @return the literalType
     */
    public TypeWrapper getLiteralType() {
        return literalType;
    }

    /**
     * {@inheritDoc}
     */
    public Collection<? extends TypeWrapper> getInnerTypes() {
        return Collections.emptyList();
    }

    /**
     * {@inheritDoc}
     */
    public TypeWrapper getEnclosingType() {
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "MetaTypeWrapper[" + this.classWrapper + "]";
    }

}
